/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fsktms.request;

import fsktms.ICloseable;
import fsktms.IMessage;
import fsktms.messagemanager.IMessageListener;
import fsktms.messagemanager.IMessageSender;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author salm
 */
public class RequestManager implements IMessageListener, ICloseable
{
    private IMessageSender msgSender;
    private final Map<Long, IRequest> requests = new HashMap<>();
    private final Map<Long, Integer> timeOuts = new HashMap<>();
    private Random hashRandom = new Random();
    private int timeOutUpdateInterval = 1000;
    
    public RequestManager(IMessageSender msgSender)
    {
        this.msgSender = msgSender;
        
        // time out thread
        (new Thread()
        {
            private RequestManager rm;
            
            @Override
            public void run()
            {
                while (!rm.isClosed())
                {
                    try {
                        Thread.sleep(timeOutUpdateInterval);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(RequestManager.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    
                    synchronized (rm.timeOuts)
                    {
                        Collection<Long> requestIDs = new ArrayList<>(rm.timeOuts.keySet());
                        for (Long rID : requestIDs)
                        {
                            if (rm.timeOuts.get(rID) - timeOutUpdateInterval > 0)
                            {
                                rm.timeOuts.put(rID, rm.timeOuts.get(rID) - timeOutUpdateInterval);
                            }
                            else
                            {
                                synchronized (rm.requests)
                                {
                                    rm.requests.remove(rID);
                                    rm.timeOuts.remove(rID);
                                }
                            }
                        }
                    }
                }
            }
            
            public Thread setData(RequestManager rman)
            {
                this.rm = rman;
                
                return this;
            }
        }).setData(this).start();
    }
    
    @Override
    public void onHasNewMessage(IMessage msg)
    {
        try {
            ObjectInputStream objIS = new ObjectInputStream(new ByteArrayInputStream(msg.getData()));
            Long msgHashID = objIS.readLong();
            if (this.requests.containsKey(msgHashID))
            {
                RequestRespond respondParams = (RequestRespond) objIS.readObject();
                if (respondParams != null)
                {
                    IRequest request = this.requests.get(msgHashID);
                    request.getRequestDelegate().requestCompleted(respondParams);
                    
                    this.requests.remove(msgHashID);
                    this.timeOuts.remove(msgHashID);
                }
            }
        } catch (IOException | ClassNotFoundException ex) {
            Logger.getLogger(RequestManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void sendRequest(IRequest request) throws IOException
    {
       long reqHashID = (long) Calendar.getInstance().getTime().hashCode() + hashRandom.nextLong();
       this.requests.put(reqHashID, request);
       this.timeOuts.put(reqHashID, request.getTimeOut());
       
       ByteArrayOutputStream bOS = new ByteArrayOutputStream();
       ObjectOutputStream objOS = new ObjectOutputStream(bOS);
       objOS.writeLong(reqHashID);
       objOS.writeObject(request.getRequestName());
       objOS.writeObject(request.getRequestParams());
       
       this.msgSender.sendMessage(bOS.toByteArray(), IRequest.REQUEST_MESSAGE_MAGICKEY);
    }

    /**
     * @return the timeOutUpdateInterval
     */
    public int getTimeOutUpdateInterval() {
        return timeOutUpdateInterval;
    }

    /**
     * @param timeOutUpdateInterval the timeOutUpdateInterval to set
     */
    public void setTimeOutUpdateInterval(int timeOutUpdateInterval) {
        this.timeOutUpdateInterval = timeOutUpdateInterval;
    }

    private boolean closed = false;
    @Override
    public boolean isClosed() {
        return this.closed;
    }

    @Override
    public void close() throws IOException
    {
        this.closed = true;
    }
}
